package cl.tinet.trainee.ejb;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

import cl.tinet.trainee.dao.ProductosDAO;
import cl.tinet.trainee.dao.ProductosDAOImpl;
import cl.tinet.trainee.exception.ConexionException;
import cl.tinet.trainee.model.CarroTO;
import cl.tinet.trainee.model.DatosMostrarTO;
import cl.tinet.trainee.model.DetalleCarroTO;
import cl.tinet.trainee.model.ProductoTO;
import cl.tinet.trainee.model.UsuarioTO;

/**
 * Session Bean implementation class CarroBean
 */
@Stateless
public class CarroBean implements CarroBeanLocal {

	/**
	 * objeto encargado de manejar las persistencias
	 */
	private ProductosDAO productosDAO;
	/**
	 * carro donde hara la compra
	 */
	private CarroTO carroTO;
	/**
	 *  producto que se agregara al carro
	 */
	private DetalleCarroTO detalleCarroTO;
	/**
	 * objeto donde se muestran los datos derivados del carro 
	 */
	private DatosMostrarTO datosMostrarTO;
	
	/**
	 * entidad que mapea la conexion a la base de datos
	 */
	@PersistenceContext(unitName = "conexionCarroCompra")
	private EntityManager em;

	/**	 
	 * @throws ConexionException
	 *             se utiliza cuando falla la persistencia a la base de datos
	 */
	@PostConstruct
	public void init() throws ConexionException {

	}

	/**
	 * {@inheritDoc}
	 */
	public int inicializarCarro() throws ConexionException {
		carroTO = new CarroTO();
		carroTO.setCompraFinalizada(0);
		carroTO.setNombreUsuario("invitado");
		return crearCarro(carroTO);

	}

	/**
	 * {@inheritDoc}
	 */
	public int obtenerIdCarro() throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		return productosDAO.obtenerIdCarro();
	}

	/**
	 * {@inheritDoc}
	 */
	public int crearCarro(CarroTO carroTO) throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		return productosDAO.crearCarro(carroTO);
	}

	/**
	 * {@inheritDoc}
	 */
	public List<ProductoTO> listarProductos(int registrosPorPagina,
			int paginaActual) throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		return productosDAO.listarProductos(registrosPorPagina, paginaActual);
	}

	/**
	 * {@inheritDoc}
	 */
	public List<ProductoTO> listarTodosProductos() throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		return productosDAO.listarTodosProductos();
	}

	/**
	 * {@inheritDoc}
	 */
	public List<DetalleCarroTO> agregarProducto(ProductoTO productoTO,
			int idCarro) throws ConexionException {

		productosDAO = new ProductosDAOImpl(em);
		carroTO = new CarroTO();
		carroTO = cargarCarro(idCarro);
		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();

		if (verificarProductoEnCarro(carroTO, productoTO)) {
			if (buscarProductoDuplicado(carroTO, productoTO) != null) {
				detalleCarroTO = buscarProductoDuplicado(carroTO, productoTO);
				detalleCarroTO.setCantidad(detalleCarroTO.getCantidad() + 1);
				actualizarDetalle(detalleCarroTO);
				lDetalleCarroTO.addAll(obtenerDetallesCarro(idCarro));
			}
		} else {
			detalleCarroTO = new DetalleCarroTO();
			detalleCarroTO.setIdCarro(carroTO);
			detalleCarroTO.setIdProducto(productoTO);
			detalleCarroTO.setCantidad(DetalleCarroTO.CANTIDAD_INICIAL);
			lDetalleCarroTO.addAll(carroTO.getlDetalleCarro());
			lDetalleCarroTO.add(detalleCarroTO);
			productosDAO.agregarProducto(detalleCarroTO);
		}
		return lDetalleCarroTO;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean verificarStockAumentar(List<DetalleCarroTO> lDetalleCarro,
			ProductoTO productoTO) {

		for (DetalleCarroTO detalleCarroTO : lDetalleCarro) {
			if (productoTO.getCodProducto().equals(
					detalleCarroTO.getIdProducto().getCodProducto())) {
				if (detalleCarroTO.getCantidad() == productoTO.getStock()) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean verificarStockDisminuir(List<DetalleCarroTO> lDetalleCarro,
			ProductoTO productoTO) {

		for (DetalleCarroTO detalleCarroTO : lDetalleCarro) {
			if (productoTO.getCodProducto().equals(
					detalleCarroTO.getIdProducto().getCodProducto())) {
				if (detalleCarroTO.getCantidad() == 1) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean verificarProductoEnCarro(CarroTO carroTO,
			ProductoTO productoTO) {

		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(carroTO.getlDetalleCarro());

		for (DetalleCarroTO detalleCarroTO : lDetalleCarroTO) {
			if (detalleCarroTO.getIdProducto().getCodProducto()
					.equals(productoTO.getCodProducto())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	public DetalleCarroTO buscarProductoDuplicado(CarroTO carroTO,
			ProductoTO productoTO) {

		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(carroTO.getlDetalleCarro());

		for (DetalleCarroTO detalleCarroTO : lDetalleCarroTO) {
			if (detalleCarroTO.getIdProducto().getCodProducto()
					.equals(productoTO.getCodProducto())) {
				return detalleCarroTO;
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	public DatosMostrarTO calcularDatosParaMostrar(int idCarro)
			throws ConexionException {

		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(obtenerDetallesCarro(idCarro));

		inicializarInstancias();
		for (DetalleCarroTO detalleCarroTO : lDetalleCarroTO) {
			datosMostrarTO.setCantidadProductos(datosMostrarTO
					.getCantidadProductos() + detalleCarroTO.getCantidad());
			datosMostrarTO.setTotalCompra(detalleCarroTO.getIdProducto()
					.getPrecio() * datosMostrarTO.getCantidadProductos());
		}
		return datosMostrarTO;
	}

	/**
	 * {@inheritDoc}
	 */
	public CarroTO cargarCarro(int idCarro) throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		CarroTO c = productosDAO.cargarCarro(idCarro);
		c.getlDetalleCarro().size();
		return c;
	}

	/**
	 * {@inheritDoc}
	 */
	public void agregarCantidad(DetalleCarroTO detalleCarroTO)
			throws ConexionException {
		detalleCarroTO.setCantidad(detalleCarroTO.getCantidad() + 1);
		actualizarDetalle(detalleCarroTO);
	}

	/**
	 * {@inheritDoc}
	 */
	public CarroTO restarCantidad(DetalleCarroTO detalleCarroTO, int idCarro)
			throws ConexionException {
		detalleCarroTO.setCantidad(detalleCarroTO.getCantidad() - 1);
		actualizarDetalle(detalleCarroTO);

		return cargarCarro(idCarro);
	}

	/**
	 * {@inheritDoc}
	 */
	public void eliminarDetalle(DetalleCarroTO detalleCarroTO)
			throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		productosDAO.eliminarDetalle(detalleCarroTO);
	}

	/**
	 * {@inheritDoc}
	 */
	public void actualizarDetalle(DetalleCarroTO detalleCarroTO)
			throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		productosDAO.actualizarDetalle(detalleCarroTO);

	}

	/**
	 * {@inheritDoc}
	 */
	public UsuarioTO identificarUsuario(UsuarioTO usuarioTO)
			throws ConexionException, NoResultException {
		productosDAO = new ProductosDAOImpl(em);
		return productosDAO.identificarUsuario(usuarioTO);
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean comprar(UsuarioTO usuarioTO, int idCarro)
			throws ConexionException {
		if (verificarStock(idCarro)) {
			productosDAO = new ProductosDAOImpl(em);
			carroTO = cargarCarro(idCarro);
			carroTO.setCompraFinalizada(1);
			carroTO.setNombreUsuario(usuarioTO.getNombreUsuario());
			carroTO.setFechaCompra(obtenerFechaActual());
			descontarStock(carroTO);
			return productosDAO.comprar(carroTO);
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean verificarStock(int idCarro) throws ConexionException {
		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(obtenerDetallesCarro(idCarro));

		List<ProductoTO> lProductoTO = new ArrayList<ProductoTO>();
		lProductoTO.addAll(listarTodosProductos());

		for (DetalleCarroTO detalleTO : lDetalleCarroTO) {
			for (ProductoTO productoTO : lProductoTO) {
				if (detalleTO.getIdProducto().getCodProducto()
						.equals(productoTO.getCodProducto())) {
					if (detalleTO.getCantidad() > productoTO.getStock()) {
						eliminarDetalle(detalleTO);
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public void descontarStock(CarroTO carroTO) throws ConexionException {

		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(carroTO.getlDetalleCarro());
		List<ProductoTO> lProductoTO = new ArrayList<ProductoTO>();
		lProductoTO.addAll(listarTodosProductos());

		for (DetalleCarroTO detalleTO : lDetalleCarroTO) {
			for (ProductoTO productoTO : lProductoTO) {
				if (detalleTO.getIdProducto().getCodProducto()
						.equals(productoTO.getCodProducto())) {
					productoTO.setStock(productoTO.getStock()
							- detalleTO.getCantidad());
					actualizarProducto(productoTO);
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void actualizarProducto(ProductoTO productoTO)
			throws ConexionException {
		productosDAO = new ProductosDAOImpl(em);
		productosDAO.actualizarProducto(productoTO);
	}

	/**
	 * {@inheritDoc}
	 */
	public List<DetalleCarroTO> obtenerDetallesCarro(int idCarro)
			throws ConexionException {

		List<DetalleCarroTO> lDetalleCarroTO = new ArrayList<DetalleCarroTO>();
		lDetalleCarroTO.addAll(cargarCarro(idCarro).getlDetalleCarro());

		return lDetalleCarroTO;
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean registrarUsuario(UsuarioTO usuarioTO)
			throws ConexionException {
		if (verificarUsuario(usuarioTO)) {
			productosDAO = new ProductosDAOImpl(em);
			return productosDAO.registrarUsuario(usuarioTO);
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @throws ConexionException
	 * @throws NoResultException
	 */
	public boolean verificarUsuario(UsuarioTO usuarioTO)
			throws NoResultException, ConexionException {

		productosDAO = new ProductosDAOImpl(em);
		if (productosDAO.obtenerUsuario(usuarioTO) != null) {
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	public Date obtenerFechaActual() {
		Calendar date = Calendar.getInstance();
		return date.getTime();
	}

	/**
	 * {@inheritDoc}
	 */
	public void inicializarInstancias() {
		datosMostrarTO = new DatosMostrarTO();
	}

	/**
	 * @return the carroTO
	 */
	public CarroTO getCarroTO() {
		return carroTO;
	}

	/**
	 * @param carroTO
	 *            the carroTO to set
	 */
	public void setCarroTO(CarroTO carroTO) {
		this.carroTO = carroTO;
	}

	/**
	 * @return the productosDAO
	 */
	public ProductosDAO getProductosDAO() {
		return productosDAO;
	}

	/**
	 * @param productosDAO
	 *            the productosDAO to set
	 */
	public void setProductosDAO(ProductosDAO productosDAO) {
		this.productosDAO = productosDAO;
	}

	/**
	 * @return the detalleCarroTO
	 */
	public DetalleCarroTO getDetalleCarroTO() {
		return detalleCarroTO;
	}

	/**
	 * @param detalleCarroTO
	 *            the detalleCarroTO to set
	 */
	public void setDetalleCarroTO(DetalleCarroTO detalleCarroTO) {
		this.detalleCarroTO = detalleCarroTO;
	}

	/**
	 * @return the em
	 */
	public EntityManager getEm() {
		return em;
	}

	/**
	 * @param em
	 *            the em to set
	 */
	public void setEm(EntityManager em) {
		this.em = em;
	}

	/**
	 * @return the datosMostrarTO
	 */
	public DatosMostrarTO getDatosMostrarTO() {
		return datosMostrarTO;
	}

	/**
	 * @param datosMostrarTO
	 *            the datosMostrarTO to set
	 */
	public void setDatosMostrarTO(DatosMostrarTO datosMostrarTO) {
		this.datosMostrarTO = datosMostrarTO;
	}
}
